﻿Public Class frmMain

    Private BusyBoxAbortTriggered As Boolean

    Public Enum LookupOperations
        GetDirectories = 1
        GetFiles = 2
        GetSingleFile = 3
    End Enum

    Public Sub UpdateTable()
        dgvArphoonContents.Rows.Clear()

        If Engine Is Nothing Then Exit Sub
        If Not Engine.IsMounted Then Exit Sub
        If Engine.MountedPackage.File Is Nothing Then Exit Sub
        If Engine.MountedPackage.File.Length = 0 Then Exit Sub

        AddLog("Retrieving file list...")

        SuspendPaint(dgvArphoonContents)

        Dim i As Integer
        For i = 0 To Engine.MountedPackage.File.Length - 1
            With Engine.MountedPackage.File(i)
                Dim validated As String
                If .ValidationPerformed = True Then
                    If .Validated = True Then validated = "OK" Else validated = "FAILED !"
                Else
                    validated = ""
                End If
                dgvArphoonContents.Rows.Add({i, .Directory, .Filename, .Extension, .Length, .Md5Hash, validated})
            End With
        Next

        ResumePaint(dgvArphoonContents, True)
    End Sub

    Public Sub AddLog(Optional ByVal text As String = "")
        If lstEngineLog.Items.Count > 512 Then
            lstEngineLog.Items.Clear()
            lstEngineLog.Items.Add("...")
        End If

        lstEngineLog.Items.Add("[TESTER] " & text)
        If (lstEngineLog.SelectedIndex > -1) Then lstEngineLog.SetSelected(lstEngineLog.SelectedIndex, False)
        lstEngineLog.SetSelected(lstEngineLog.Items.Count - 1, True)
        lstEngineLog.Refresh()
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
#If FULL_DEBUG Then
        tscLookupMode.Items.Add("MFT Offsets")
#End If

        MainFormReference = Me
        panelBusy.BringToFront()
        BusyBoxAbortTriggered = False

        tscLookupMode.SelectedIndex = 1
    End Sub

    Private Sub dgvArphoonContents_CellDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvArphoonContents.CellDoubleClick
        If dgvArphoonContents.SelectedRows Is Nothing Then Exit Sub
        If dgvArphoonContents.SelectedRows.Count = 0 Then Exit Sub

        Try
            Dim index As Integer
            index = dgvArphoonContents.SelectedRows(0).Cells.Item(0).Value
            sfdDestination.Filter = "Specified by Packed File|*." & Engine.MountedPackage.File(index).Extension & "|All files|*.*"
            sfdDestination.FileName = ""
            sfdDestination.ShowDialog()

            If sfdDestination.FileName = "" Then Exit Sub

            AddLog("Trying to unpack selected file...")

            Dim pfStream As Arps.PackagedFileStream
            pfStream = Engine.MountedPackage.File(index).GetStream
            If pfStream.ValidationPerformed = False Then
                AddLog("Packed file Validation not performed")
                If pfStream.Validate() Then
                    AddLog("Packed file validation succeed")
                Else
                    AddLog("WARNING: Packed file validation failed")
                End If
            Else
                If pfStream.Validated = True Then
                    AddLog("Packed file validation succeed")
                Else
                    AddLog("WARNING: Packed file validation failed")
                End If
            End If


            Dim fs As New IO.FileStream(sfdDestination.FileName, IO.FileMode.Create, IO.FileAccess.ReadWrite, IO.FileShare.None)
            Dim bw As New IO.BinaryWriter(fs)

            ' Buffered Reader
            Dim buffer() As Byte
            Dim maxBufferSize As Integer = 4194304 ' 4 Mb
            Do
                buffer = pfStream.ReadBytes(maxBufferSize)
                If buffer Is Nothing Then Exit Do
                If buffer.Length = 0 Then Exit Do
                bw.Write(buffer)
                If Not buffer.Length = maxBufferSize Then Exit Do
            Loop

            If fs.Position = pfStream.Length Then
                AddLog("Buffered data dump completed successfully")
            Else
                AddLog("CRITICAL: Lenght of data Dumped dont match stream lenght.")
            End If

            pfStream.Close()
            pfStream = Nothing
            bw.Close()
            bw = Nothing
            fs.Close()
            fs = Nothing
        Catch ex As Exception
            AddLog("Exception while trying to unpack selected file.")
            AddLog("EX: " & ex.Message)
        End Try

        lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1
    End Sub

    Private Sub cmdBusyAbort_Click(sender As System.Object, e As System.EventArgs) Handles cmdBusyAbort.Click
        BusyBoxAbortTriggered = True
        cmdBusyAbort.Enabled = False
    End Sub

    Private Sub tsbOpenPackage_Click(sender As Object, e As EventArgs) Handles tsbOpenPackage.Click
        lstEngineLog.Items.Clear()
        dgvArphoonContents.Rows.Clear()
        Engine = New Arps.Engine

        ofdSource.FileName = ""
        ofdSource.ShowDialog()

        If ofdSource.FileName = "" Then Exit Sub

        AddLog("Beggining mounting of file " & ofdSource.FileName)

        Dim password As String = ""

        Try
            Dim protocol As String
            protocol = Engine.GetPackageProtocol(ofdSource.FileName)

            AddLog("Package compiled using protocol " & protocol)

            If Engine.HaveEncryptionSupport(protocol) Then
                AddLog("Password required.")
                password = InputBox("Package is Encrypted and Password Protected. Please input decryption password:", "Package Encrypted")
            End If
        Catch ex As Exception
            AddLog("Error: Failed to identify target file.")
            AddLog("   ex: " & ex.Message)
            Exit Sub
        End Try

        Dim returnCode As Arps.Engine.PackageMountReturnCode
        returnCode = Engine.Mount(ofdSource.FileName, password)

        AddLog("ReturnCode is: " & returnCode)

        UpdateTable()

        If Engine Is Nothing Then Exit Sub
        If Not Engine.IsMounted Then Exit Sub
        If Engine.MountedPackage.File Is Nothing Then Exit Sub
        If Engine.MountedPackage.File.Length = 0 Then Exit Sub

        AddLog(Engine.MountedPackage.File.Length & " file(s) registered.")

        lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1

        tsbValidateContents.Enabled = True
        tsbUnmountPackage.Enabled = True
        tsbVirtualizeTrees.Enabled = True
        tsbLookup.Enabled = True
        tsbEnumerateShadows.Enabled = True
        tsbLoadShadows.Enabled = True
    End Sub

    Private Sub tsbUnmountPackage_Click(sender As Object, e As EventArgs) Handles tsbUnmountPackage.Click
        If Engine Is Nothing Then Exit Sub

        AddLog("Unmounting package...")
        dgvArphoonContents.Rows.Clear()
        Engine.Unmount()
        Engine.Dispose()
        Engine = Nothing

        AddLog("Done")

        tsbUnmountPackage.Enabled = False
        tsbValidateContents.Enabled = False
        tsbVirtualizeTrees.Enabled = False
        tsbLookup.Enabled = False
        tsbEnumerateShadows.Enabled = False
        tsbLoadShadows.Enabled = False

        lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1
    End Sub

    Private Sub tsbValidateContents_Click(sender As Object, e As EventArgs) Handles tsbValidateContents.Click
        If Engine Is Nothing Then Exit Sub

        AddLog("Validating Package contents...")

        If Engine.IsMounted = False Then
            AddLog("No package mounted")
            Exit Sub
        End If

        If Engine.MountedPackage.File.Length = 0 Then
            AddLog("No files inside the mounted package")
            Exit Sub
        End If

        panelBusy.Visible = True
        pbBusyProgress.Value = 0
        tsbUnmountPackage.Enabled = False
        tsbLookup.Enabled = False
        tsbOpenPackage.Enabled = False
        tsbVirtualizeTrees.Enabled = False
        tsbValidateContents.Enabled = False
        tscLookupMode.Enabled = False
        BusyBoxAbortTriggered = False
        tsbEnumerateShadows.Enabled = False
        tsbLoadShadows.Enabled = False
        Dim reducedOutput As Boolean = (Engine.MountedPackage.File.Length > 500)


        Dim i As Integer
        For i = 0 To Engine.MountedPackage.File.Length - 1
            lblBusyStatus.Text = "Validating file " & i + 1 & " of " & Engine.MountedPackage.File.Length & "..."
            Dim percent As Byte = Fix(((i + 1) * 100) / Engine.MountedPackage.File.Length)
            If percent > 100 Then percent = 100
            pbBusyProgress.Value = percent
            panelBusy.Refresh()
            Application.DoEvents()

            If BusyBoxAbortTriggered Then
                AddLog("Aborted.")
                BusyBoxAbortTriggered = False
                Exit For
            End If

            If Not reducedOutput Then AddLog("[" & i + 1 & "/" & Engine.MountedPackage.File.Length & "] Validating file...")
            If Engine.FileValidation(i).ValidationPerformed = True Then
                If Not reducedOutput Then AddLog("      Validation step already performed. Last state is: " & Engine.FileValidation(i).Validated)
            Else
                Dim output As Boolean = Engine.FileValidation(i).Validate
                If (Not reducedOutput) Or (Not output) Then AddLog("      File " & i & " validation returned: " & output)
            End If
        Next

        panelBusy.Visible = False
        tsbUnmountPackage.Enabled = True
        tsbLookup.Enabled = True
        tsbOpenPackage.Enabled = True
        tsbVirtualizeTrees.Enabled = True
        tsbValidateContents.Enabled = True
        tscLookupMode.Enabled = True
        cmdBusyAbort.Enabled = True
        tsbEnumerateShadows.Enabled = True
        tsbLoadShadows.Enabled = True

        AddLog("Package contents validation ended successfully. Updating Table...")
        UpdateTable()
        AddLog("Done.")

        lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1
    End Sub

    Private Sub tsbVirtualizeTrees_Click(sender As Object, e As EventArgs) Handles tsbVirtualizeTrees.Click
        If Engine Is Nothing Then Exit Sub

        AddLog("Virtualization status:")
        AddLog("Directory Tree: " & Engine.IsDirectoryTreeBuilt)
        AddLog("File Tree: " & Engine.IsFileTreeBuilt)

        AddLog("Rebuilding...")

        AddLog("...Directory tree...")
        Engine.BuildDirectoryTreeStructure()

        AddLog("...File tree...")
        Engine.BuildFileTreeStructure()

        AddLog("Done.")

        lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1
    End Sub

    Private Sub tsbLookup_Click(sender As Object, e As EventArgs) Handles tsbLookup.Click
        If Engine Is Nothing Then Exit Sub

#If FULL_DEBUG Then
        If LCase(Trim(tscLookupMode.Text)) = "mft offsets" Then
            tscLookupMode.Text = "MFT Offsets"
            If (dgvArphoonContents.SelectedRows Is Nothing) OrElse (dgvArphoonContents.SelectedRows.Count < 1) Then
                AddLog("No Row selected!")
                Exit Sub
            End If
            Dim packedFile As Arps.Utilities.PackagedFileInformationCollection = Engine.MountedPackage.File(dgvArphoonContents.SelectedRows.Item(0).Cells(0).Value)
            Dim file As Arps.InternalDebug.PackedFileInternalInformation = Arps.InternalDebug.GetPackedFileInformation(packedFile)
            Dim frmPFi As New frmPackedFileInformation(file, packedFile.Filename, packedFile.Directory)
            frmPFi.Show()
            Exit Sub
        End If
#End If

        Dim lookupString As String
        lookupString = InputBox("Lookup string", "Arps")

        lookupString = Trim(lookupString)
        If lookupString = "" Then Exit Sub

        AddLog("Starting lookup sequence for: " & lookupString)

        Dim mode As LookupOperations
        Select Case LCase(Trim(tscLookupMode.Text))
            Case "directories", "folders"
                AddLog("   Mode= multiple directories")
                tscLookupMode.Text = "Directories"
                mode = LookupOperations.GetDirectories
            Case "files"
                AddLog("   Mode= multiple files")
                tscLookupMode.Text = "Files"
                mode = LookupOperations.GetFiles
            Case "single file", "file"
                AddLog("   Mode= single file")
                tscLookupMode.Text = "Single File"
                mode = LookupOperations.GetSingleFile
            Case Else
                AddLog("   Mode= multiple directories")
                tscLookupMode.Text = "Directories"
                mode = LookupOperations.GetDirectories
        End Select

        Dim newForm As ItemLookup
        Dim path As String
        Dim filter As String
        Try
            lookupString = lookupString.Replace("/", "\")
            lookupString = lookupString.Replace("\\", "\")
            lookupString = Trim(lookupString)
            Dim tmp() As String = Split(lookupString, "\")
            If Not (tmp(tmp.Length - 1).Contains("*") Or tmp(tmp.Length - 1).Contains("?")) Then
                path = Arps.Utilities.ConsolidateParameters(tmp, 0, tmp.Length - 1, "\")
                filter = ""
                Exit Try
            End If
            If tmp.Length = 1 Then
                path = ""
                filter = tmp(tmp.Length - 1)
            Else
                path = Arps.Utilities.ConsolidateParameters(tmp, 0, tmp.Length - 2, "\")
                filter = tmp(tmp.Length - 1)
            End If
        Catch ex As Exception
            Exit Sub
        End Try
        AddLog("   Parsed Path: " & path)
        AddLog("   Parsed Filter: " & filter)
        Select Case mode
            Case LookupOperations.GetDirectories
                Dim buffer() As String = Engine.GetDirectories(path, filter)
                If buffer Is Nothing Then AddLog("No items returned. Aborted") : lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1 : Exit Sub
                If buffer.Length = 0 Then AddLog("No items returned. Aborted") : lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1 : Exit Sub
                newForm = New ItemLookup
                newForm.dgvTable.Columns.Clear()
                newForm.dgvTable.Columns.Add("dgvColDirectoryName", "Directory Name")
                newForm.dgvTable.Columns.Item(0).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
                newForm.lblLookup.Text = path & " // " & filter
                newForm.dgvTable.Update()
                Dim i As Integer
                For i = 0 To buffer.Length - 1
                    newForm.dgvTable.Rows.Add(buffer(i))
                Next
                AddLog(buffer.Length & " items returned.")
                newForm.Show()
            Case LookupOperations.GetFiles
                Dim buffer() As Arps.Utilities.PackagedFileInformationCollection = Engine.GetFiles(path, filter)
                If buffer Is Nothing Then AddLog("No items returned. Aborted") : lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1 : Exit Sub
                If buffer.Length = 0 Then AddLog("No items returned. Aborted") : lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1 : Exit Sub
                newForm = New ItemLookup
                Dim i As Integer
                For i = 0 To buffer.Length - 1
                    With buffer(i)
                        Dim validated As String
                        If .ValidationPerformed = True Then
                            If .Validated = True Then validated = "OK" Else validated = "FAILED !"
                        Else
                            validated = ""
                        End If
                        newForm.dgvTable.Rows.Add({i, .Directory, .Filename, .Extension, .Length, .Md5Hash, validated})
                        newForm.lblLookup.Text = path & " // " & filter
                    End With
                Next
                AddLog(buffer.Length & " items returned.")
                newForm.Show()
            Case LookupOperations.GetSingleFile
                Dim buffer As Arps.Utilities.PackagedFileInformationCollection = Engine.GetFile(path & "\" & filter)
                If buffer.FullName = "" Then
                    AddLog("No items returned. Aborted")
                    lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1
                    Exit Sub
                End If
                Try
                    newForm = New ItemLookup
                    With buffer
                        Dim validated As String
                        If .ValidationPerformed = True Then
                            If .Validated = True Then validated = "OK" Else validated = "FAILED !"
                        Else
                            validated = ""
                        End If
                        newForm.dgvTable.Rows.Add({0, .Directory, .Filename, .Extension, .Length, .Md5Hash, validated})
                        newForm.lblLookup.Text = path & " // " & filter
                    End With
                    AddLog("Item found.")
                    newForm.Show()
                Catch ex As Exception
                    AddLog("Exception raised while adding the new item to the Results window.")
                    AddLog("EX: " & ex.Message)
                End Try
        End Select
        lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1
    End Sub

    Private Sub tsbEnumerateShadows_Click(sender As Object, e As EventArgs) Handles tsbEnumerateShadows.Click
        Dim results As List(Of String)
        If tstxtShadowConvention.TextLength < 1 Then
            results = Engine.EnumerateShadows()
        Else
            results = Engine.EnumerateShadows(tstxtShadowConvention.Text)
        End If

        If results Is Nothing OrElse results.Count < 1 Then
            MsgBox("No valid shadows found with the specified convention", MsgBoxStyle.Critical, "Shadow Enumeration")
            Exit Sub
        End If

        If ShadowsListForm Is Nothing OrElse ShadowsListForm.IsDisposed Then ShadowsListForm = New frmShadowList()
        ShadowsListForm.lstShadowList.Items.Clear()
        ShadowsListForm.lstShadowList.Items.AddRange(results.ToArray())

        Dim i As Long
        For i = 0 To ShadowsListForm.lstShadowList.Items.Count - 1
            ShadowsListForm.lstShadowList.SetSelected(i, True)
        Next

        ShadowsListForm.Show()
    End Sub

    Private Sub tsbLoadShadows_Click(sender As Object, e As EventArgs) Handles tsbLoadShadows.Click
        Dim results As List(Of String)
        If tstxtShadowConvention.TextLength < 1 Then
            results = Engine.EnumerateShadows()
        Else
            results = Engine.EnumerateShadows(tstxtShadowConvention.Text)
        End If

        If results Is Nothing OrElse results.Count < 1 Then
            MsgBox("No valid shadows found with the specified convention", MsgBoxStyle.Critical, "Shadow Enumeration")
            Exit Sub
        End If

        LoadShadow(results.ToArray())
    End Sub

    Friend Sub LoadShadow(ByVal files() As String)
        If Engine Is Nothing Then Exit Sub

        AddLog("Attaching shadows...")

        If Engine.IsMounted = False Then
            AddLog("No package mounted")
            Exit Sub
        End If

        If files Is Nothing OrElse files.Length < 1 Then
            AddLog("No files to attach")
            Exit Sub
        End If

        Dim password As String
        password = InputBox("Please input decryption password for shadow packages (optional, leave blank for none)", "Decryption Password")
        Dim access As Arps.Engine.ShadowAccessRights
        Dim f As New frmShadowAccessSelection()
        f.ShowDialog(Me)
        access = f.Value
        f.Dispose()
        f = Nothing

        If access = 0 Then
            MsgBox("Invalid access descriptor for shadow files", MsgBoxStyle.Critical, "Invalid Access")
            Exit Sub
        End If

        panelBusy.Visible = True
        pbBusyProgress.Value = 0
        tsbUnmountPackage.Enabled = False
        tsbLookup.Enabled = False
        tsbOpenPackage.Enabled = False
        tsbVirtualizeTrees.Enabled = False
        tsbValidateContents.Enabled = False
        tscLookupMode.Enabled = False
        BusyBoxAbortTriggered = False
        tsbEnumerateShadows.Enabled = False
        tsbLoadShadows.Enabled = False

        Dim i As Integer
        For i = 0 To files.Length - 1
            Dim result As Arps.Engine.PackageMountReturnCode
            Dim fullName As String
            Dim fi As New System.IO.FileInfo(Engine.MountedPackage.Package.Filename)
            fi = New IO.FileInfo(fi.DirectoryName & "\" & files(i))
            fullName = fi.FullName
            result = Engine.AttachShadow(fullName, password, access)

            If Not result = Arps.Engine.PackageMountReturnCode.MountSuccess Then
                Dim msgresult As MsgBoxResult
                msgresult = MsgBox("A shadow failed to attach properly to the engine." & Chr(13) & Chr(10) & Chr(13) & Chr(10) & "Filename: " & files(i) & Chr(13) & Chr(10) & "Code: " & result & Chr(13) & Chr(10) & Chr(13) & Chr(10) & "Continue loading shadows?", MsgBoxStyle.YesNo, "Shadow loading failed")
                If msgresult = MsgBoxResult.No Then Exit For
            End If
        Next

        panelBusy.Visible = False
        tsbUnmountPackage.Enabled = True
        tsbLookup.Enabled = True
        tsbOpenPackage.Enabled = True
        tsbVirtualizeTrees.Enabled = True
        tsbValidateContents.Enabled = True
        tscLookupMode.Enabled = True
        cmdBusyAbort.Enabled = True
        tsbEnumerateShadows.Enabled = True
        tsbLoadShadows.Enabled = True

        AddLog("Shadow attach procedure ended successfully. Updating Table...")
        UpdateTable()
        AddLog("Done.")

        lstEngineLog.SelectedIndex = lstEngineLog.Items.Count - 1
    End Sub
End Class
